package com.framework.core.mybatisplus;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.framework.core.common.annotation.DataScope;
import com.framework.core.common.bo.LoginUser;
import com.framework.core.common.bo.SysRoleBO;
import com.framework.core.common.bo.SysUserBO;
import com.framework.core.common.enums.DataScopeEnum;
import com.framework.core.util.SecurityUtil;
import lombok.SneakyThrows;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SubSelect;

import java.util.Collections;
import java.util.Objects;

/**
 * 自定义数据权限
 *
 * @author Kevin
 */
public class CustomDataPermissionHandler implements DataPermissionHandler {

    /**
     * 构建过滤条件
     *
     * @param user  当前登录用户
     * @param where 当前查询条件
     * @return 构建后查询条件
     */
    public static Expression dataScopeFilter(SysUserBO user, DataScope data, Expression where) {
        String tableAlias = data.tableAlias();
        String deptColumnName = data.deptColumnName();
        String userColumnName = data.userColumnName();
        Expression expression = null;
        for (SysRoleBO role : user.getRoles()) {
            String dataScope = role.getDataScope();
            if (DataScopeEnum.ALL.getCode().equals(dataScope)) {
                return where;
            }
            if (DataScopeEnum.CUSTOM.getCode().equals(dataScope)) {
                InExpression inExpression = new InExpression();
                inExpression.setLeftExpression(buildColumn(tableAlias, deptColumnName));
                SubSelect subSelect = new SubSelect();
                PlainSelect select = new PlainSelect();
                select.setSelectItems(Collections.singletonList(new SelectExpressionItem(new Column("dept_id"))));
                select.setFromItem(new Table("sys_role_dept"));
                EqualsTo equalsTo = new EqualsTo();
                equalsTo.setLeftExpression(new Column("role_id"));
                equalsTo.setRightExpression(new LongValue(role.getId()));
                select.setWhere(equalsTo);
                subSelect.setSelectBody(select);
                inExpression.setRightExpression(subSelect);
                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, inExpression) : inExpression;
            }
            if (DataScopeEnum.DEPT.getCode().equals(dataScope)) {
                EqualsTo equalsTo = new EqualsTo();
                equalsTo.setLeftExpression(buildColumn(tableAlias, deptColumnName));
                equalsTo.setRightExpression(new LongValue(user.getDeptId()));
                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, equalsTo) : equalsTo;
            }
            if (DataScopeEnum.DEPT_AND_CHILD.getCode().equals(dataScope)) {
                InExpression inExpression = new InExpression();
                inExpression.setLeftExpression(buildColumn(tableAlias, deptColumnName));
                SubSelect subSelect = new SubSelect();
                PlainSelect select = new PlainSelect();
                select.setSelectItems(Collections.singletonList(new SelectExpressionItem(new Column("dept_id"))));
                select.setFromItem(new Table("sys_dept"));
                EqualsTo equalsTo = new EqualsTo();
                equalsTo.setLeftExpression(new Column("dept_id"));
                equalsTo.setRightExpression(new LongValue(user.getDeptId()));
                Function function = new Function();
                function.setName("find_in_set");
                function.setParameters(new ExpressionList(new LongValue(user.getDeptId()), new Column("ancestors")));
                select.setWhere(new OrExpression(equalsTo, function));
                subSelect.setSelectBody(select);
                inExpression.setRightExpression(subSelect);
                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, inExpression) : inExpression;
            }
            if (DataScopeEnum.SELF.getCode().equals(dataScope)) {
                EqualsTo equalsTo = new EqualsTo();
                equalsTo.setLeftExpression(buildColumn(tableAlias, userColumnName));
                equalsTo.setRightExpression(new LongValue(user.getId()));
                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, equalsTo) : equalsTo;
            }
        }
        return ObjectUtils.isNotEmpty(where) ? new AndExpression(where, new Parenthesis(expression)) : expression;
    }

    /**
     * 构建Column
     *
     * @param tableAlias 表别名
     * @param columnName 字段名称
     * @return 带表别名字段
     */
    public static Column buildColumn(String tableAlias, String columnName) {
        if (StringUtils.isNotEmpty(tableAlias)) {
            columnName = tableAlias + "." + columnName;
        }
        return new Column(columnName);
    }

    @Override
    @SneakyThrows
    public Expression getSqlSegment(Expression where, String mappedStatementId) {
        Class<?> clazz = Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(".")));
        String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(".") + 1);
        DataScope dataScope = AnnotationUtil.getAnnotation(ReflectUtil.getMethodByName(clazz, methodName), DataScope.class);
        if (Objects.isNull(dataScope)) {
            return where;
        }
        LoginUser loginUser = SecurityUtil.getLoginUser();
        if (Objects.isNull(loginUser) || SecurityUtil.isRootGroup()) {
            return where;
        }
        return dataScopeFilter(loginUser.getSysUser(), dataScope, where);
    }
}
